#include <QikListBoxModel.h>
#include <QikListBox.h>
#include <MQikListBoxData.h>
#include <QikListBoxData.h>
#include <QikContent.h>
#include <QikZoomDlg.h>
#include <QUiConfigClient.h>
#include <QikCommand.h>

#include "PinQListView.h"
#include "PinQDocument.h"
#include "PinQTask.h"
#include "PinQExternalInterface.h"
#include <PinQ.mbg>
#include <PinQ.rsg>

_LIT(KMbmPath, "\\Private\\E0070805\\PinQ.mbm");

CPinQListView* CPinQListView::NewLC(CQikAppUi& aAppUi,
		const TVwsViewId aParentViewId)
	{
	CPinQListView* self = new (ELeave) CPinQListView(aAppUi,aParentViewId);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CPinQListView::CPinQListView(CQikAppUi& aAppUi, const TVwsViewId aParentViewId) :
	CQikViewBase(aAppUi, aParentViewId),
			iCommandManager(CQikCommandManager::Static(*iCoeEnv)),
			iConfigClient(CQUiConfigClient::Static(*iCoeEnv))
	{
	}

CPinQListView::~CPinQListView()
	{
	SetCategoryModel(NULL);
	}

void CPinQListView::ConstructL()
	{
	BaseConstructL();
	}

TVwsViewId CPinQListView::ViewId() const
	{
	return TVwsViewId(KUidPinQApp, KUidPinQListView);
	}

void CPinQListView::HandleCommandL(CQikCommand& aCommand)
	{
	CPinQAppUi* appUi = static_cast<CPinQAppUi*>(iCoeEnv->AppUi());
	if ((aCommand.Type() == EQikCommandTypeCategory) && (aCommand.Id()
			!= EPinQListViewEditCategoriesCmd))
		{
		appUi->HandleCategoryChangeL(aCommand.CategoryHandle());
		return;
		}
	switch (aCommand.Id())
		{
		case EPinQListViewNewCmd:
			{
			NewItemCmdL();
			break;
			}
		case EPinQListViewEditCmd:
			{
			ViewCmdL();
			break;
			}
		case EPinQListViewPacketsCmd:
			{
			PacketsCmdL();
			break;
			}
		case EPinQListViewDeleteCmd:
		case EPinQListViewDeleteHardwareCmd:
			{
			DeleteItemCmdL();
			break;
			}
		case EPinQListViewUndoDeleteCmd:
			{
			UndoDeleteCmdL();
			break;
			}
		case EPinQZoomCmd:
			{
			CPinQDocument* document =
					static_cast<CPinQDocument*>(iQikAppUi.Document());
			const TInt zoomFactor =
					CQikZoomDialog::RunDlgLD(document->Preferences().ListViewZoomState());

			if (document->Preferences().SetListViewZoomState(zoomFactor))
				{
				SetZoomFactorL(zoomFactor);
				}
			break;
			}
		case EPinQListViewEditCategoriesCmd:
			{
			appUi->EditCategoryL();
			break;
			}
		default:
			CQikViewBase::HandleCommandL(aCommand);
			break;
		}
	}

void CPinQListView::HandleListBoxEventL(CQikListBox* aListBox,
		TQikListBoxEvent aEventType, TInt aItemIndex, TInt /*aSlotId*/)
	{
	/*
	 _LIT(dbg1,"Here %d, Ch %d");
	 TBuf<256> buf;
	 buf.Format(dbg1,aEventType,EEventSelectionChanged);
	 CEikonEnv::Static()->InfoMsg(buf);
	 */
	switch (aEventType)
		{
		case EEventHighlightMoved:
			{
			UpdateTaskCmdL();
			break;
			}
		case EEventSelectionChanged:
			{
			/*
			 TQikInteractionStyle interactionStyle = iConfigClient.CurrentConfig().InteractionStyle();
			 if(interactionStyle == EQikInteractionStyleSoftkey)
			 */
			HandleSelectionChanged(*aListBox);
			break;
			}
		case EEventItemConfirmed:
		case EEventItemTapped:
			{
			UpdateTaskStateL(aItemIndex);
			break;
			}
		default:
			break;
		}
	}

void CPinQListView::ViewActivatedL(const TVwsViewId& /*aPrevViewId*/,
		const TUid aCustomMessageId, const TDesC8& aCustomMessage)
	{

	if (aCustomMessageId == KUidPinQDnlItem)
		{
		TPinQDnlItemBuf buf;
		buf.Copy(aCustomMessage);
		TUid currentUid = buf().iUid;

		if (buf().iState == EUpdateItem)
			ItemUpdatedL(currentUid);
		else
			//EDelete
			ItemDeletedL(currentUid);
		}
	}

CQikCommand* CPinQListView::DynInitOrDeleteCommandL(CQikCommand* aCommand,
		const CCoeControl& /*aControlAddingCommands*/)
	{
	const TInt id = aCommand->Id();
	switch (id)
		{
		case EQikListBoxCmdMark:
		case EQikListBoxCmdUnmark:
		case EQikListBoxCmdMarkAll:
		case EQikListBoxCmdUnmarkAll:
			{
			aCommand->SetNamedGroupId(EPinQListViewMarkCmd);
			break;
			}
		case EQikSoftkeyCmdSelectCategory:
			{
			aCommand->SetPriority(EPinQCategoryCmdPrio);
			aCommand->SetGroupId(EPinQSettingsCmdGroupId);
			break;
			}
		case EQikListBoxCmdSelect:
			{
			UpdateTaskCmdL();
			break;
			}
		default:
			break;
		}
	return aCommand;
	}

void CPinQListView::ViewConstructL()
	{
	ViewConstructFromResourceL(R_PINQ_LISTVIEW_CONFIGURATIONS);

	CPinQDocument* document = static_cast<CPinQDocument*>(iQikAppUi.Document());
	SetZoomFactorL(document->Preferences().ListViewZoomState());

	// Get all categories and put them in the CategoryModel
	CQikCategoryModel* categoryModel = static_cast<CPinQAppUi*>(iCoeEnv->AppUi())->CategoryModelInstance();
	TInt count=PinQModel().CategoryCount();
	for (TInt index = 0; index < count; index++)
		{
		TPinQCategory category = PinQModel().Category(index);
		categoryModel->AddCategoryL(category.CategoryHandle(),
				category.CategoryName());
		}

	CQikListBox* listBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	RefreshListBoxL(*listBox);
	listBox->SetListBoxObserver(this);

	SetCategoryModel(categoryModel);
	SetCategoryModelAsCommandsL();
	}

void CPinQListView::SaveL()
	{
	CPinQDocument* document = static_cast<CPinQDocument*>(iQikAppUi.Document());
	if (document->Preferences().IsDirty() || PinQModel().IsDirty())
		{
		document->SaveL();
		}
	}

void CPinQListView::UpdateTaskStateL(TInt aItemIndex)
	{
	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	const TInt currentIndex = ListBox->CurrentItemIndex();

	ASSERT(currentIndex> KErrNotFound);
	CPinQTask& task=PinQModel().Task(TUid::Uid(ListBox->ItemIdL(currentIndex)));
	CQikContent* icon= NULL;
	//TInt text;
	switch (task.State())
		{
		//case CPinQTask::ENone:
		default:
			{
			task.Start(PinQAppUi());
			/*	text=R_PINQ_LISTVIEW_STOP_COMMAND_TEXT;
			 icon = CQikContent::NewL(this, KMbmPath, EMbmPinqRunning,
			 EMbmPinqRunningmask);*/
			break;
			}/*
			 case CPinQTask::ERunning:
			 {
			 task.SetState(CPinQTask::EStopped, PinQAppUi());
			 text=R_PINQ_LISTVIEW_RESET_COMMAND_TEXT;
			 icon = CQikContent::NewL(this, KMbmPath, EMbmPinqStopped,
			 EMbmPinqStoppedmask);
			 break;
			 }
			 case CPinQTask::EStopped:
			 {
			 task.SetState(CPinQTask::ENone, PinQAppUi());
			 text=R_PINQ_LISTVIEW_START_COMMAND_TEXT;
			 icon = CQikContent::NewL(this, KMbmPath, EMbmPinqNone,
			 EMbmPinqNonemask);
			 break;
			 }
			 default:
			 ASSERT(0);*/
		}
	if (icon)
		{
		MQikListBoxModel& model(ListBox->Model());
		model.ModelBeginUpdateLC();
		MQikListBoxData* ListBoxData = model.RetrieveDataL(aItemIndex);
		CleanupClosePushL(*ListBoxData);

		CleanupStack::PushL(icon);
		//iCommandManager.SetTextL(*this, EQikListBoxCmdSelect, text);
		ListBoxData->SetIconL(icon, EQikListBoxSlotLeftSmallIcon1);
		CleanupStack::Pop(icon);
		icon=NULL;

		CleanupStack::PopAndDestroy(ListBoxData);
		model.ModelEndUpdateL();
		}
	}
void CPinQListView::UpdateTaskCmdL()
	{
	CQikListBox* listBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);

	return;
	
	//code below is buggy on AddNew
	//because of evnt handled before data populated
	const TInt currentIndex = listBox->CurrentItemIndex();

	if (currentIndex > KErrNotFound)
	//List is not empty
		{
		TInt id=listBox->ItemIdL(currentIndex);
		CPinQTask& task=PinQModel().Task(TUid::Uid(id));
		switch (task.State())
			{/*
			 case CPinQTask::ENone:
			 {
			 iCommandManager.SetTextL(*this, EQikListBoxCmdSelect, 
			 R_PINQ_LISTVIEW_START_COMMAND_TEXT);
			 break;
			 }
			 case CPinQTask::ERunning:
			 {
			 iCommandManager.SetTextL(*this, EQikListBoxCmdSelect, 
			 R_PINQ_LISTVIEW_STOP_COMMAND_TEXT);
			 break;
			 }
			 case CPinQTask::EStopped:
			 {
			 iCommandManager.SetTextL(*this, EQikListBoxCmdSelect, 
			 R_PINQ_LISTVIEW_RESET_COMMAND_TEXT);
			 break;
			 }*/
			}
		}
	else
		{
		//List is empty
		iCommandManager.SetTextL(*this, EQikListBoxCmdSelect, 
		R_PINQ_LISTVIEW_NEW_COMMAND_TEXT);
		}
	}

void CPinQListView::HandleSelectionChanged(const CQikListBox& aListBox)
	{
	// Change where list box command mark and unmark will be placed in softkey style
	const TInt count = aListBox.SelectionIndexes().Count();
	// Decide if mark and unmark shall be in right soft key or in more menu
	iCommandManager.SetType(*this, EQikListBoxCmdMark,
			count ? EQikCommandTypeDone : EQikCommandTypeItem);
	iCommandManager.SetType(*this, EQikListBoxCmdUnmark,
			count ? EQikCommandTypeDone : EQikCommandTypeItem);
	// Decide if mark and unmark shall be in a cascading menu or not
	iCommandManager.SetNamedGroupId(*this, EQikListBoxCmdMark, count ? 0
			: EPinQListViewMarkCmd);
	iCommandManager.SetNamedGroupId(*this, EQikListBoxCmdUnmark, count ? 0
			: EPinQListViewMarkCmd);
	}

void CPinQListView::PacketsCmdL()
	{
	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	const TInt currentIndex = ListBox->CurrentItemIndex();
	ASSERT(currentIndex> KErrNotFound);
	const TInt id = ListBox->ItemIdL(currentIndex);

	TPinQDnlItem entry;
	entry.iUid = TUid::Uid(id);
	TPinQDnlItemBuf buf(entry);
	iQikAppUi.ActivateViewL(TVwsViewId(KUidPinQApp, KUidPinQPacketsView),
			KUidPinQDnlItem, buf);
	}

void CPinQListView::ViewCmdL()
	{

	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	const TInt currentIndex = ListBox->CurrentItemIndex();
	ASSERT(currentIndex> KErrNotFound);
	const TInt id = ListBox->ItemIdL(currentIndex);

	// DNL to the detail view with a custom message containing with actual 
	// state and items id.
	TPinQDnlItem entry;
	entry.iState = EUpdateItem;
	entry.iUid = TUid::Uid(id);
	TPinQDnlItemBuf buf(entry);
	iQikAppUi.ActivateViewL(TVwsViewId(KUidPinQApp, KUidPinQTaskView),
			KUidPinQDnlItem, buf);
	}

void CPinQListView::NewItemCmdL()
	{
	TVwsViewId viewId = TVwsViewId(KUidPinQApp, KUidPinQTaskView);
	iQikAppUi.ActivateViewL(viewId);
	}

void CPinQListView::DeleteItemCmdL()
	{
	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	MQikListBoxModel& model(ListBox->Model());

	TBool acceptDelete = EFalse;

	const RArray<TInt>& selectionArray = ListBox->SelectionIndexes();
	TInt count = selectionArray.Count();

	if (count > 1)
		{
		acceptDelete = iEikonEnv->QueryWinL(R_PINQ_DIALOG_DELETE_TITLE, R_PINQ_DIALOG_DELETE_LABEL_TEXT);
		}
	else
		{
		acceptDelete
				= iEikonEnv->QueryWinL(R_PINQ_DIALOG_DELETE_UNDOABLE_TITLE, R_PINQ_DIALOG_DELETE_UNDOABLE_LABEL_TEXT);
		}

	if (acceptDelete)
		{
		if (!count)
			{
			const TInt currentIndex = ListBox->CurrentItemIndex();

			if (currentIndex > KErrNotFound)
				{
				PinQModel().RemoveTask(TUid::Uid(ListBox->ItemIdL(currentIndex)),
						CPinQModel::EBackup);
				ListBox->RemoveItemL(currentIndex);
				}
			}
		else
			{
			while (count--) //assume selectionArray sorted asc
				{
				PinQModel().RemoveTask(
						TUid::Uid(ListBox->ItemIdL(selectionArray[count])),
						CPinQModel::ENoBackup);
				ListBox->RemoveItemL(selectionArray[count]);
				}
			}
		iCommandManager.SetAvailable(*this, EPinQListViewUndoDeleteCmd,
				PinQModel().DeletedTaskExist());

		UpdateDeleteCmdL(ListBox->ItemCount());

		SaveL();
		}
	}

void CPinQListView::UndoDeleteCmdL()
	{
	PinQModel().RecoverDeletedTaskL();
	RefreshListBoxL();
	iCommandManager.SetAvailable(*this, EPinQListViewUndoDeleteCmd, EFalse);
	SaveL();
	}

void CPinQListView::ItemDeletedL(const TUid& aUid)
	{
	const CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	MQikListBoxModel& model(ListBox->Model());
	const TInt index = model.ItemIdIndex(aUid.iUid);

	ASSERT(index> KErrNotFound);
	model.ModelBeginUpdateLC();
	model.RemoveDataL(index);
	model.ModelEndUpdateL();

	iCommandManager.SetAvailable(*this, EPinQListViewUndoDeleteCmd, ETrue);
	UpdateDeleteCmdL(ListBox->ItemCount());
	}

void CPinQListView::ItemUpdatedL(TUid aTaskUid)
	{
	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	MQikListBoxModel& model(ListBox->Model());
	TInt index = model.ItemIdIndex(aTaskUid.iUid);
	CPinQTask& task=PinQModel().Task(aTaskUid);

	model.ModelBeginUpdateLC();
	if (index==KErrNotFound)
		AddTaskL(model, task);
	else
		{
		MQikListBoxData* ListBoxData = model.RetrieveDataL(index);
		CleanupClosePushL(*ListBoxData);
		ListBoxData->SetTextL(task.Pref().Name(), EQikListBoxSlotText1);
		ListBoxData->SetTextL(task.StateName(), EQikListBoxSlotText2);
		CleanupStack::PopAndDestroy(ListBoxData);
		}
	model.DataUpdatedL(index);
	TLinearOrder<MQikListBoxData> compare(CPinQListView::Compare);
	model.Sort(compare);
	ListBox->SetCurrentItemIndexL(model.ItemIdIndex(aTaskUid.iUid), ETrue,
			EDrawNow);
	model.ModelEndUpdateL();
	
	UpdateDeleteCmdL(ListBox->ItemCount());
	}

void CPinQListView::RefreshListBoxL() // const
	{
	//const 
	CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	RefreshListBoxL(*ListBox);
	}
void CPinQListView::RefreshListBoxL(const CQikListBox& aListBox)// const
	{
	MQikListBoxModel& model(aListBox.Model());

	model.ModelBeginUpdateLC();
	const TInt count = PinQModel().TaskCount();
	const TInt currentCategoryHandle = static_cast<CPinQAppUi&>(iQikAppUi).CurrentCategoryHandle();

	for (TInt i = 0; i < count; i++)
		{
		CPinQTask& task = PinQModel().Task(i);
		const TInt index = model.ItemIdIndex(task.Uid().iUid);
		const CPinQTask::CPref& pref=task.Pref();

		if (index == KErrNotFound)
			{
			if ((currentCategoryHandle == EPinQCategoryAll) || (pref.Category()
					== currentCategoryHandle))
				AddTaskL(model, task);
			}

		if (currentCategoryHandle != EPinQCategoryAll)
			{
			if ((index != KErrNotFound) && (pref.Category()
					!= currentCategoryHandle))
				{
				model.RemoveDataL(index);
				}
			}

		}
	TLinearOrder<MQikListBoxData> compare(CPinQListView::Compare);
	model.Sort(compare);
	model.ModelEndUpdateL();
	UpdateDeleteCmdL(aListBox.ItemCount());
	}

void CPinQListView::AddTaskL(MQikListBoxModel& aModel, CPinQTask& aTask) const
	{
	const CPinQTask::CPref& pref=aTask.Pref();
	MQikListBoxData* ListBoxData =
			aModel.NewDataL(MQikListBoxModel::EDataNormal);
	CleanupClosePushL(*ListBoxData);
	ListBoxData->AddTextL(pref.Name(), EQikListBoxSlotText1);
	ListBoxData->AddTextL(aTask.StateName(), EQikListBoxSlotText2);
	ListBoxData->SetItemId(aTask.Uid().iUid);

	CQikContent* icon = CQikContent::NewL(this, KMbmPath, EMbmPinqNone,
			EMbmPinqNonemask);
	CleanupStack::PushL(icon);
	ListBoxData->AddIconL(icon, EQikListBoxSlotLeftSmallIcon1);
	CleanupStack::Pop(icon);
	icon=NULL;

	CleanupStack::PopAndDestroy(ListBoxData);
	}

void CPinQListView::UpdateDeleteCmdL(TBool aAvailable) const
	{
	iCommandManager.SetAvailable(*this, EPinQListViewDeleteCmd, aAvailable);
	iCommandManager.SetAvailable(*this, EPinQListViewDeleteHardwareCmd,
			aAvailable);
	iCommandManager.SetAvailable(*this, EPinQListViewEditCmd, aAvailable);
	iCommandManager.SetAvailable(*this, EPinQListViewPacketsCmd, aAvailable);
	}

void CPinQListView::SetZoomFactorL(TInt aZoomLevel)
	{
	const TInt zoomFactor = CQikAppUi::ZoomFactorL(aZoomLevel, *iEikonEnv);
	CQikViewBase::SetZoomFactorL(zoomFactor);
	}

TInt CPinQListView::Compare(const MQikListBoxData& aFirst,
		const MQikListBoxData& aSecond)
	{
	return aFirst.Text(EQikListBoxSlotText1).CompareC(aSecond.Text(EQikListBoxSlotText1));
	}

void CPinQListView::TaskStateChangedL(const CPinQTask& aTask,
		CPinQTask::TState aNewState) // const

	{

	_LIT(K1stPcktFmt, "%S: n/a");
	_LIT(KNthPcktFmt, "%S:%d %d.%03d");
	//seq time
	_LIT(KStateFmt, "%S");
	_LIT(KErrorTOFmt, "%S: t/o");
	_LIT(KErrorTO2Fmt, "%S:%d t/o");
	TBuf<128> buf;

	if (aTask.PacketsCount())
		{//there's some number of packets
		ASSERT((aTask.State()!=CPinQTask::EOpenning || aNewState==CPinQTask::ESending)
				&& aTask.State()!=CPinQTask::ELookingUp);
		
		CPinQTask::TPacket* last=aTask.LastPacket();
		ASSERT(last);
		if (aTask.Error()==CPinQTask::ETimeout)
			{// timeout during sending or receiving last packet triggered by delay
			ASSERT((aTask.State()==CPinQTask::EReceiving||aTask.State()==CPinQTask::ESending)
					&& (aNewState==CPinQTask::EDelay||aNewState==CPinQTask::EStopped));
			buf.Format(KErrorTO2Fmt, 
					&CPinQTask::StateName(aNewState==CPinQTask::EDelay?aTask.State():aNewState),
					last->iSeq);
			}
		else
			{
			if (aNewState==CPinQTask::EDelay)
				{
				ASSERT(aTask.State()==CPinQTask::EReceived);
				return;
				}
			//last packet so far so good
			CPinQTask::TPacket* previous=aTask.PreviousPacket();
			if (!previous && aNewState!=CPinQTask::EReceived)
				{//very first packet was not received yet
				ASSERT(aTask.PacketsCount()==1);
				buf.Format(K1stPcktFmt, &CPinQTask::StateName(aNewState));
				}
			else
				{
				if (aNewState==CPinQTask::EReceived)
					{
					buf.Format(
							KNthPcktFmt, //new packet just received
							&CPinQTask::StateName(aNewState), last->iSeq,
							last->iTime /1000, last->iTime % 1000);
					}
				else
					{//not very first packet in progress
					ASSERT(previous);
					if (aNewState==CPinQTask::EDelay)
						{//delay after packet received - keep message
						ASSERT(aTask.State()==CPinQTask::EReceived);
						return;
						}
					if (previous->iError==CPinQTask::ETimeout)
						{//previous packet was timed out
						buf.Format(KErrorTO2Fmt,
								&CPinQTask::StateName(aNewState),
								last->iSeq);
						}
					else
						{//say previous packet time
						buf.Format(KNthPcktFmt,
								&CPinQTask::StateName(aNewState), last->iSeq,
								previous->iTime /1000, previous->iTime % 1000);
						}

					}
				}
			}
		}
	else
		{// No packets
		ASSERT(aTask.State()==CPinQTask::EOpenning
				|| aTask.State()==CPinQTask::ELookingUp
				|| aTask.State()==CPinQTask::EStopped
		);
		if (aTask.Error()==CPinQTask::ETimeout)
			{
			ASSERT(aTask.State()!=CPinQTask::EStopped);
			buf.Format(KErrorTOFmt, &CPinQTask::StateName(aTask.State()));
			}
		else
			{
			buf.Format(KStateFmt, &CPinQTask::StateName(aTask.State()));
			}
		}

	SetSlot2L(buf, aTask.Uid());
	}

void CPinQListView::SetSlot2L(const TDesC& aText, TUid aUid)// const
	{
	const CQikListBox* ListBox =
			LocateControlByUniqueHandle<CQikListBox>(EPinQListViewListCtrl);
	MQikListBoxModel& model(ListBox->Model());
	TInt index = model.ItemIdIndex(aUid.iUid);

	ASSERT(index> KErrNotFound);
	model.ModelBeginUpdateLC();
	MQikListBoxData* ListBoxData = model.RetrieveDataL(index);
	CleanupClosePushL(*ListBoxData);
	ListBoxData->SetTextL(aText, EQikListBoxSlotText2);
	CleanupStack::PopAndDestroy(ListBoxData);
	model.DataUpdatedL(index);
	model.ModelEndUpdateL();
	}
// vim: ts=2:sw=2
